package com.kinyx.framework.data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.ReturnNull;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.exception.KRuntimeException;
import com.kinyx.framework.utils.KArrayUtils;

/**
 * 对象映射工具类
 */
class KObjectMapper {

	/**
	 * 默认值
	 * @param xml true: XML; false: JSON;
	 * @param nullable true: 允许null值; false: 不允许null值;
	 * @param isArray true: 数组; false: 对象;
	 * @return 默认值
	 */
	@ReturnNull("nullable == true")
	static String defaultValue(@CanNotNull final boolean xml, @CanNotNull final boolean nullable, @CanNotNull final boolean isArray) {
		if (nullable) { return null; }
		return xml ? "" : (isArray ? "[]" : "{}");
	}

	@ReturnNotNull
	@Throws(at = "KObjectMapper.writeValueAsString(ObjectMapper, Object)", value = "JsonProcessingException", when = "ObjectMapper.writeValueAsString(Object)")
	static String writeValueAsString(@CanNotNull final ObjectMapper mapper, @CanNotNull final Object obj) {
		try {
			final String result = mapper.writeValueAsString(obj);
			if (mapper instanceof XmlMapper) {
				// 列表类型，将内层item标签转为对象类型标签。
				if (obj instanceof List) {
					@SuppressWarnings("rawtypes")
					final String className = ((List) obj).get(0).getClass().getSimpleName();
					return result.replaceAll("<item>", "<" + className + ">").replaceAll("</item>", "</" + className + ">");
				}
			}
			return result;
		} catch (final JsonProcessingException e) {
			throw new KRuntimeException(e);
		}
	}

	@ReturnNotNull
	@Throws(at = "KObjectMapper.toBean(ObjectMapper, String, Class)", value = "JsonMappingException", when = "ObjectMapper.readValue(String, Class)")
	@Throws(at = "KObjectMapper.toBean(ObjectMapper, String, Class)", value = "JsonProcessingException", when = "ObjectMapper.readValue(String, Class)")
	static <T> T toBean(@CanNotNull final ObjectMapper mapper, @CanNotNull final String str, @CanNotNull final Class<T> clazz) {
		try {
			return mapper.readValue(str, clazz);
		} catch (final JsonMappingException e) {
			throw new KRuntimeException(e);
		} catch (final JsonProcessingException e) {
			throw new KRuntimeException(e);
		}
	}

	@ReturnNotNull
	@Throws(at = "KObjectMapper.toHashMap(ObjectMapper, String)", value = "JsonMappingException", when = "ObjectMapper.readValue(String, JavaType)")
	@Throws(at = "KObjectMapper.toHashMap(ObjectMapper, String)", value = "JsonProcessingException", when = "ObjectMapper.readValue(String, JavaType)")
	static HashMap<String, String> toHashMap(@CanNotNull final ObjectMapper mapper, @CanNotNull String str) {
		if (mapper instanceof XmlMapper) {
			if (str.startsWith("<HashMap>") && str.endsWith("</HashMap>")) {
				str = str.substring("<HashMap>".length(), str.length() - "</HashMap>".length());
			}
		}
		try {
			return mapper.readValue(str, mapper.getTypeFactory().constructParametricType(HashMap.class, String.class, String.class));
		} catch (final JsonMappingException e) {
			throw new KRuntimeException(e);
		} catch (final JsonProcessingException e) {
			throw new KRuntimeException(e);
		}
	}

	@ReturnNotNull
	@Throws(at = "KObjectMapper.toArrayList(ObjectMapper, String, Class)", value = "JsonMappingException", when = "ObjectMapper.readValue(String, JavaType)")
	@Throws(at = "KObjectMapper.toArrayList(ObjectMapper, String, Class)", value = "JsonProcessingException", when = "ObjectMapper.readValue(String, JavaType)")
	static <T> ArrayList<T> toArrayList(@CanNotNull final ObjectMapper mapper, @CanNotNull final String str, @CanNotNull final Class<T> clazz) {
		try {
			return mapper.readValue(str, mapper.getTypeFactory().constructParametricType(ArrayList.class, clazz));
		} catch (final JsonMappingException e) {
			throw new KRuntimeException(e);
		} catch (final JsonProcessingException e) {
			throw new KRuntimeException(e);
		}
	}

	@ReturnNotNull
	@Throws(at = "KObjectMapper.toJsonNode(ObjectMapper, String)", value = "JsonMappingException", when = "ObjectMapper.readTree(String)")
	@Throws(at = "KObjectMapper.toJsonNode(ObjectMapper, String)", value = "JsonProcessingException", when = "ObjectMapper.readTree(String)")
	static JsonNode toJsonNode(@CanNotNull final ObjectMapper mapper, @CanNotNull final String str) {
		try {
			return mapper.readTree(str);
		} catch (final JsonMappingException e) {
			throw new KRuntimeException(e);
		} catch (final JsonProcessingException e) {
			throw new KRuntimeException(e);
		}
	}

	@ReturnNotNull
	@Throws(at = "KObjectMapper.deepGet(ObjectMapper, String, String...)", value = "JsonMappingException", when = "ObjectMapper.readTree(String)")
	@Throws(at = "KObjectMapper.deepGet(ObjectMapper, String, String...)", value = "JsonProcessingException", when = "ObjectMapper.readTree(String)")
	static JsonNode deepGet(@CanNotNull final ObjectMapper mapper, @CanNotNull final String str, @CanNull final String... keys) {
		JsonNode jsonNode = null;
		try {
			jsonNode = mapper.readTree(str);
		} catch (final JsonMappingException e) {
			throw new KRuntimeException(e);
		} catch (final JsonProcessingException e) {
			throw new KRuntimeException(e);
		}
		if (!KArrayUtils.isEmpty(keys)) {
			for (final String key : keys) {
				jsonNode = jsonNode.get(key);
			}
		}
		return jsonNode;
	}

}
